package http

import (
	"crypto/tls"
	"fmt"
	"gitee.com/zhongguo168a/go-nodex/nodex"
	"gitee.com/zhongguo168a/go-nodex/nodex/models"
	"gitee.com/zhongguo168a/go-nodex/nodex/server"
	"gitee.com/zhongguo168a/go-nodex/nodex/server/message"
	"gitee.com/zhongguo168a/gocodes/datax"
	"gitee.com/zhongguo168a/gocodes/datax/jsonmap"
	"gitee.com/zhongguo168a/gocodes/myx/errorx"
	"gitee.com/zhongguo168a/gocodes/myx/logx"
	"github.com/dyson/certman"
	"github.com/gin-gonic/gin"
	"github.com/pkg/errors"
	"log"
	"net"
	"net/http"
	"os"
)

// iServer 接口实现，定义一个Server服务类
type Server struct {
	//服务器的名称
	name string
	//
	config *models.ServerConfig
	//服务绑定的IP地址
	IP string
	//服务绑定的端口
	Port string
	//当前Server的消息管理模块，用来绑定MsgId和对应的处理方法
	msgHandler server.IMsgHandle
	//该Server的连接创建时Hook函数
	OnConnStart func(conn server.IConnection)
	//该Server的连接断开时的Hook函数
	OnConnStop func(conn server.IConnection)
	//
	httpServer *gin.Engine
}

const NAME = "http"

func init() {
	nodex.RegisterServerCreator(NAME, NewServer)
}

/*
创建一个服务器句柄
*/
func NewServer(conf *models.ServerConfig) server.IServer {
	s := &Server{
		name:   conf.Name,
		IP:     conf.Host,
		Port:   conf.Port,
		config: conf,
	}

	handler := &MsgHandle{}
	handler.MsgHandle = *nodex.NewMsgHandle(s)
	s.SetMsgHandle(handler)
	return s
}
func (s *Server) AddRouter(msgId string, router server.IRouter) {
	s.msgHandler.AddRouter(msgId, router)
}

// ============== 实现 ziface.IServer 里的全部接口方法 ========
func (s *Server) Name() string {
	return s.name
}

func (s *Server) GetConfig() *models.ServerConfig {
	return s.config
}

func (s *Server) SetMsgHandle(val server.IMsgHandle) {
	s.msgHandler = val
}

func (s *Server) postHandler(c *gin.Context, route server.IRouter, routeKey string) {
	var (
		err error
	)
	defer func() {
		if err != nil {
			logx.Info(fmt.Sprintf("connection closed at reader: %v", err.Error()))
		}
	}()
	_ = route
	msgString := routeKey

	c.Request.ParseForm()
	m := map[string]interface{}{}
	for k, v := range c.Request.PostForm {
		if len(v) > 0 {
			m[k] = v[0]
		} else {
			m[k] = ""
		}
	}

	err = s.handler(c, route, msgString, m)
	if err != nil {
		return
	}
}

func (s *Server) handler(c *gin.Context, route server.IRouter, msgName string, m map[string]interface{}) (err error) {
	msg := &message.HttpMessage{
		MsgId: msgName,
		Data:  m,
	}
	//得到当前客户端请求的Request数据
	req := &server.Request{
		Context: c,
		Message: msg,
		Token:   c.GetHeader("Authorization"),
	}
	{
		router := s.msgHandler.GetRoute(msg.MsgId)
		if router == nil {
			err = errors.New(fmt.Sprintf("router not found [msg=%v]", msg.MsgId))
			return
		}
		req.Route = router
		// 处理参数
		args, newerr := router.NewArgs()
		if newerr != nil {
			err = errors.Wrap(newerr, "new args")
			return
		}
		err = jsonmap.MapToStruct(m, args)
		if err != nil {
			err = errors.Wrap(err, "map to struct")
			return
		}
		req.Args = args
	}

	// 从绑定好的消息和对应的处理方法中执行对应的Handle方法
	doerr := s.msgHandler.Do(req)
	if doerr != nil {
		err = errors.Wrap(doerr, "do message")
		return
	}

	return
}

func (s *Server) getHandler(c *gin.Context, route server.IRouter, routeKey string) {
	var (
		err error
	)
	defer func() {
		if err != nil {
			logx.Info(fmt.Sprintf("connection closed at reader: %v", err.Error()))
		}
	}()
	_ = route
	msgString := routeKey
	m := map[string]interface{}{}
	for key, vals := range c.Request.URL.Query() {
		if len(vals) == 1 {
			m[key] = vals[0]
		} else {
			m[key] = vals
		}
	}
	err = s.handler(c, route, msgString, m)
	if err != nil {
		return
	}
}

// Start 开启网络服务
func (s *Server) Start() {
	fmt.Printf("[Start] start http server[%s] at %s:%s\n", s.Name(), s.IP, s.Port)

	gin.SetMode(gin.ReleaseMode)
	r := gin.Default()
	r.Use(gin.Recovery())
	r.Use(cors())
	r.GET("/echo/:msg", func(c *gin.Context) {
		msgString := c.Param("msg")
		c.String(http.StatusOK, msgString)
	})

	for key, val := range s.msgHandler.GetRouters() {
		route := val
		routekey := key

		if route.GetEnableHttpGet() {
			r.GET("/"+key, func(context *gin.Context) {
				s.getHandler(context, route, routekey)
			})
		}

		r.POST("/"+key, func(context *gin.Context) {
			s.postHandler(context, route, routekey)
		})
	}

	//e
	s.httpServer = r

	addr := fmt.Sprintf("%s:%s", s.IP, s.Port)
	ln, err := net.Listen("tcp", addr)
	if err != nil {
		err = errorx.Wrap(err, "net.Listen", datax.M{"addr": addr})
		return
	}
	if s.config.CertFile != "" || s.config.KeyFile != "" {
		cm, certerr := certman.New(s.config.CertFile, s.config.KeyFile)
		if certerr != nil {
			err = errorx.Wrap(certerr, "new certman")
			return
		}
		logger := log.New(os.Stdout, "", log.LstdFlags)
		cm.Logger(logger)
		if err = cm.Watch(); err != nil {
			err = errorx.Wrap(certerr, "watch certman")
			return
		}
		fmt.Println("Use certman to watch ssl file")

		config := &tls.Config{}
		config.NextProtos = []string{"http/1.1"}
		config.GetCertificate = cm.GetCertificate

		ln = tls.NewListener(ln, config)
	}

	go r.RunListener(ln)
}

// 跨域中间件
func cors() gin.HandlerFunc {
	return func(c *gin.Context) {
		method := c.Request.Method
		origin := c.GetHeader("Origin")
		if origin != "" {
			// 可将将* 替换为指定的域名
			c.Header("Access-Control-Allow-Origin", "*")
			c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
			c.Header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization")
			c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type")
			c.Header("Access-Control-Allow-Credentials", "true")
		}

		if method == "OPTIONS" {
			c.AbortWithStatus(http.StatusNoContent)
		}

		c.Next()
	}
}

// 停止服务
func (s *Server) Stop() {
	fmt.Println(fmt.Sprintf("[STOP] stop websocket server[%v]", s.name))
}

// 运行服务
func (s *Server) Serve() {
	s.Start()
	//TODO Server.Serve() 是否在启动服务的时候 还要处理其他的事情呢 可以在这里添加
	//阻塞,否则主Go退出， listenner的go将会退出
	select {}
}

// 得到链接管理
func (s *Server) GetConnMgr() server.IConnManager {
	return nil
}

// 设置该Server的连接创建时Hook函数
func (s *Server) SetOnConnStart(hookFunc func(server.IConnection)) {
	s.OnConnStart = hookFunc
}

// 设置该Server的连接断开时的Hook函数
func (s *Server) SetOnConnStop(hookFunc func(server.IConnection)) {
	s.OnConnStop = hookFunc
}

// 调用连接OnConnStart Hook函数
func (s *Server) CallOnConnStart(conn server.IConnection) {
	if s.OnConnStart != nil {
		//fmt.Println("---> CallOnConnStart....")
		s.OnConnStart(conn)
	}
}

// 调用连接OnConnStop Hook函数
func (s *Server) CallOnConnStop(conn server.IConnection) {
	if s.OnConnStop != nil {
		//fmt.Println("---> CallOnConnStop....")
		s.OnConnStop(conn)
	}
}

func (s *Server) GetHttpServer() *gin.Engine {
	return s.httpServer
}
